﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Linq;
namespace RStein.Posterous.API
{
    //TODO: Předpokládám, že tato třída postupně splyne s IPosterousPost
    internal class TwitterPost : EntityWithClientStateBase<TwitterPost>, ITwitterPost
    {
        #region private fields
        private List<string> m_mediaNames;
        private List<MediaStreamInfo> m_mediasInfos;

        #endregion private fields
        public TwitterPost(ITwitterAccount account) : base(PosterousEntityState.New)
        {
            if (account == null)
            {
                throw new ArgumentNullException("account");
            }

            TwitterAccount = account;
            Title = Body = Url = MediaId = String.Empty;
            m_mediaNames = new List<string>();
            m_mediasInfos = new List<MediaStreamInfo>();
        }

        #region methods

        public bool Equals(TwitterPost other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }
            if (ReferenceEquals(this, other))
            {
                return true;
            }
            return base.Equals(other) && Equals(other.Url, Url);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            return Equals(obj as TwitterPost);
        }

        public override int GetHashCode()
        {
            
            return (Url != null ? Url.GetHashCode() : base.GetHashCode());
            
        }

        public static bool operator ==(TwitterPost left, TwitterPost right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(TwitterPost left, TwitterPost right)
        {
            return !Equals(left, right);
        }

        protected void OnPostInfoLoaded(EventArgsValue<ISinglePostInfo> e)
        {
            EventHandler<EventArgsValue<ISinglePostInfo>> handler = PostInfoLoaded;
            if (handler != null)
            {
                Thread.CurrentThread.GetCurrentSynchronizationContext().Post(o => handler(this, e), null);
            }
        }


        private void checkState(PosterousEntityState reqState, string exMessage)
        {
            if (State != reqState)
            {
                throw new InvalidOperationException(exMessage);
            }
        }

        #region Overrides of EntityWithClientStateBase<TwitterPost>

        protected override void SaveUpdated()
        {
            Debug.Assert(false);
        }

        protected override void SaveNew()
        {
            var builder = SimpleWebFactory.Instance.Create<IPosterousRequestBuilder>();
            builder.StartBuildWebRequest(AutopostToTwitter ? 
                                                               GlobalConstants.URL_ADD_AND_TWIT_TWITTER_POST :
                                                                                                                 GlobalConstants.URL_ADD_TWITTER_POST);
            
            builder.BuildFormData(GlobalConstants.TWITTER_POST_USERNAME, TwitterAccount.UserName);
            builder.BuildFormData(GlobalConstants.TWITTER_POST_PASSWORD, ((TwitterAccount)(TwitterAccount)).Password);
            conditionalRequestValueWrite(builder, GlobalConstants.TWITTER_POST_TITLE, Title);
            conditionalRequestValueWrite(builder, GlobalConstants.TWITTER_POST_BODY, Body);
                                    
            addMedia(builder);

            processResponse(builder);
            m_mediasInfos.ForEach(media => media.DisposeFunction(media.Stream));
            m_mediasInfos.Clear();
            SuppressChanges = true;
        }

        private void processResponse(IPosterousRequestBuilder builder)
        {
            WebResponse response = builder.GetResponse();
            var parser = new TwitterPostResponseParser();
            var responseBuilder = new PosterousResponseBuilder();
            var vals = responseBuilder.ParseResponse(response, parser.GetResults);
            MediaId = vals[GlobalConstants.XML_MEDIA_ID_ELEMENT.ToString()];
            Url = vals[GlobalConstants.XML_MEDIA_URL_ELEMENT.ToString()];
        }

        private void addMedia(IPosterousRequestBuilder builder)
        {
            m_mediaNames.ForEach(builder.BuildFormDataFromFile);
            m_mediasInfos.ForEach(media => builder.BuildFormDataFromStream(media.Stream, media.MediaName));
        }

        private void conditionalRequestValueWrite(IPosterousRequestBuilder builder,  string key, string value)
        {
            if (!String.IsNullOrEmpty(value))
            {
                builder.BuildFormData(key, value);    
            }
        }

        #endregion
        #endregion methods
        
        #region Implementation of ITwitterPost

        public string Url 
        {
            get; 
            set; 
        }
        public string Title
        {
            get; 
            set;
        }
        
        public string Body 
        { 
            get; 
            set; 
        }

        public string MediaId
        {
            get; 
            set;
        }

        public bool AutopostToTwitter
        {
            get;
            set;
        }
        public ITwitterAccount TwitterAccount
        {
            get;
            set;
        }

        public IEnumerable<string> MediaNames
        {
            get
            {
                return m_mediaNames;
            }
        }

        
        public virtual void AppendMedium(string filePath)
        {
            checkState(PosterousEntityState.New, GlobalConstants.INVALID_IN_STATE_LOADED_EX);

            if ((String.IsNullOrEmpty(filePath)) ||
                !File.Exists(filePath))
            {
                throw new ArgumentException("fileName");
            }

            m_mediaNames.Add(filePath);
        }

        
        public virtual ISinglePostInfo GetPostInfo()
        {
            checkState(PosterousEntityState.Loaded, GlobalConstants.INVALID_IN_STATE_NEW_EX);
            var bitLyService = TwitterAccount.Application.BitLyService;
            string bitLySuffix = new Uri(Url).GetBitLySuffix();
            var post = bitLyService.GetSinglePost(bitLySuffix);
            OnPostInfoLoaded(new EventArgsValue<ISinglePostInfo>(post, null));
            return post;
            
        }

        public virtual void GetPostInfoAsync()
        {
            Action<Exception> exchandler = e =>
                                               {
                                                   new PosterousException(GlobalConstants.ASYNC_EXCEPTION_TEXT, e);
                                                   OnPostInfoLoaded(new EventArgsValue<ISinglePostInfo>(null, e));
                                               };

            AsyncHelper.RunDelegateAsync((Func<ISinglePostInfo>) GetPostInfo, exchandler, null);     
        }

        public event EventHandler<EventArgsValue<ISinglePostInfo>> PostInfoLoaded;

        
        #endregion

        #region Implementation of IPostMediaStreamFunctions

        public virtual void AppendMedium(Stream stream, string mediaName, Action<Stream> releaseStreamFunction)
        {
            checkMediaStream(stream, mediaName);
            checkState(PosterousEntityState.New, GlobalConstants.INVALID_IN_STATE_LOADED_EX);
            var mediaInfo = new MediaStreamInfo(stream, mediaName, releaseStreamFunction ?? delegate {});
            m_mediasInfos.Add(mediaInfo);
            m_mediaNames.Concat(new[]
                                    {
                                        mediaName
                                    });
            SetDirty();
        }

        public virtual void AppendMedium(Stream stream, string mediaName)
        {
            AppendMedium(stream, mediaName);
        }

        private void checkMediaStream(Stream stream, string mediaName)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (string.IsNullOrEmpty(mediaName))
            {
                throw new ArgumentException("mediaName");
            }
        }
        #endregion
    }
}